import re
import datetime
import rest_framework

from django.conf import settings
from rest_framework_simplejwt.tokens import TokenError, Token, RefreshToken

from models.user import User
from models.warn import WebWarnings, WebInboxMails, WebReceivers
from models.global_config import WebGlobalConfig
from rest_framework_mongoengine.serializers import DocumentSerializer, serializers
from apps.warn_center.display import Tips
from utilities.regex import NAME_RE, DOMAIN_RE, IP_RE, IP_PORT_RE, SCHEMA_RE
from utilities.redis import captcha_redis as redis
from utilities.djconsole import djconsole


class WarningSerializer(DocumentSerializer):
    target_group_id = serializers.CharField()
    task_session_id = serializers.CharField()
    target_id = serializers.CharField()
    task_type = serializers.CharField()
    content = serializers.CharField()
    user_id = serializers.CharField()

    class Meta:
        model = WebWarnings
        fields = [
            'target_group_id',
            'target_id',
            'task_type',
            'content',
            'task_session_id',
            'user_id'
        ]


class InboxMailSerializer(DocumentSerializer):
    inbox_mail_id = serializers.CharField(read_only=True)
    title = serializers.CharField(read_only=True)
    status = serializers.ChoiceField(choices=WebInboxMails.status.choices)
    content = serializers.CharField(read_only=True)
    created_at = serializers.DateTimeField(read_only=True)
    user_id = serializers.CharField(read_only=True)

    class Meta:
        model = WebInboxMails
        fields = ['inbox_mail_id', 'title', 'status', 'content', 'created_at', 'user_id']

    def bulk_write(self, data_list):
        if data_list:
            self.Meta.model.objects.bulk_write(data_list)


class ReceiversSerializer(DocumentSerializer):
    receiver_id = serializers.CharField(read_only=True)
    contact_name = serializers.CharField(required=False, allow_blank=False, max_length=50)
    receiver_email = serializers.EmailField()
    sender = serializers.SerializerMethodField()
    receiver_type = serializers.IntegerField(read_only=True)
    user_id = serializers.CharField()

    def __init__(self, *args, **kwargs):
        super(ReceiversSerializer, self).__init__(*args, **kwargs)
        web_global_config = WebGlobalConfig.objects.find_one()
        if web_global_config:
            self._sender_cache = web_global_config.email_settings.email
        else:
            self._sender_cache = ""

    def validate_contact_name(self, contact_name):
        tips = Tips()

        if not re.match(NAME_RE, contact_name):
            raise serializers.ValidationError(tips.get(1))

        if self.Meta.model.objects.filter(contact_name=contact_name).first():
            raise serializers.ValidationError(tips.get(0, (contact_name,)))

        return contact_name

    def update(self, instance, validated_data):
        validated_data.pop('contact_name', None)
        if instance.receiver_type == 0:
            raise serializers.ValidationError("Can't edit default receiver.")
        instance.update(**validated_data)
        return instance

    def create(self, validated_data):
        license_info = djconsole.get_license_detail(getattr(self, 'request'))
        max_receiver_num = int(license_info.get('max_receiver_number', settings.DEFAULT_RECEIVER_NUMBER))
        if self.Meta.model.objects.count() - 1 >= max_receiver_num:
            raise serializers.ValidationError(Tips().get(3, (max_receiver_num, )))

        return super(ReceiversSerializer, self).create(validated_data)

    def get_sender(self, validated_data):
        return self._sender_cache

    class Meta:
        model = WebReceivers
        fields = ['receiver_id', 'contact_name', 'receiver_email', 'sender', 'receiver_type', 'user_id']


class WarnEmailSerializer(DocumentSerializer):
    class Meta:
        model = WebGlobalConfig
        fields = [
            'server_host',
            'server_port',
            'email',
            'password',
            'encrypt',
            'email_prefix',
        ]

    server_host = serializers.CharField()
    server_port = serializers.IntegerField()
    email = serializers.EmailField()
    password = serializers.CharField()
    encrypt = serializers.ChoiceField(
        choices=Meta.model.email_settings.default.encrypt.choices
    )
    email_prefix = serializers.CharField(required=False, allow_blank=True)

    def update(self, instance, validated_data):
        instance.update(email_settings=validated_data)

        return instance

    def create(self, validated_data):
        return self.Meta.model(email_settings=validated_data).save()


class WarnEmailTestSerializer(DocumentSerializer):
    class Meta:
        model = WebGlobalConfig
        fields = ['title', 'content', 'receiver_ids']

    title = serializers.CharField(max_length=16)
    content = serializers.CharField(max_length=64)
    receiver_ids = serializers.ListField()

    @staticmethod
    def validate_receiver_ids(receiver_id_list):
        return [
            item['receiver_email']
            for item in WebReceivers.objects.find(
                {'receiver_id': {'$in': receiver_id_list}}
            )
        ]


class WarnNetMapSerializer(DocumentSerializer):
    class Meta:
        model = WebGlobalConfig
        fields = ['net_map_disabled', 'net_map_host']

    net_map_disabled = serializers.BooleanField(default=True)
    net_map_host = serializers.CharField(max_length=Meta.model.net_map_host.max_length, required=False)

    def validate_net_map_host(self, net_map_host):
        if self.initial_data.get('net_map_disabled', False):
            return net_map_host
        tips_text = Tips().get(2)
        if not re.match(SCHEMA_RE, net_map_host):
            raise serializers.ValidationError(tips_text)

        try:
            serializers.URLField().run_validation(net_map_host)
        except rest_framework.exceptions.ValidationError:
            raise serializers.ValidationError(tips_text)

        return net_map_host


def get_ip_from_djconsole():
    request = FakeRequest()
    res = djconsole.get_network_list(request)

    ip = None
    for item in res.get('ports', []):
        if item.get('up', False):
            if item.get('type', 'work') == 'mgmt':
                continue
            ip = item.get('ipv4_addr')

    return ip


def get_token():
    fake_token = redis.get('fake_token:notify')
    if not fake_token:
        obj_token = FakeToken()
    else:
        try:
            obj_token = FakeToken(fake_token)
            obj_token.check_exp()
        except TokenError:
            obj_token = FakeToken()

    return f"Bearer {str(obj_token)}"


def get_host_addr():
    global_config = WebGlobalConfig.objects.find_one()
    if global_config and not global_config.net_map_disabled:
        host_addr = global_config.net_map_host
    else:
        host_addr = f"{get_ip_from_djconsole()}"

    if not host_addr.startswith("http://") and not host_addr.startswith("https://"):
        return f"http://{host_addr}"
    return host_addr


class FakeToken(Token):
    token_type = 'access'
    lifetime = datetime.timedelta(days=1)

    def __init__(self, token=None):
        if not token:
            user = User.objects.filter(username='admin', is_superuser=True).first()
            token = RefreshToken.for_user(user).access_token
            token.set_exp(lifetime=datetime.timedelta(days=1))
            token = str(token)
            redis.set('fake_token:notify', token)

        super(FakeToken, self).__init__(token)


class FakeRequest(object):
    def __init__(self):
        self.META = {'HTTP_AUTHORIZATION': get_token()}
